home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / DeviceView / DeviceView.cpp next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  30.0 KB  |  833 lines

  1. //-----------------------------------------------------------------------------
  2. // File: DeviceView.cpp
  3. //
  4. // Desc: A simple custom device configuration UI using the DIDevImage framework
  5. //
  6. // Copyright (c) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #define DIRECTINPUT_VERSION 0x0800
  10.  
  11. #include <windows.h>
  12. #include <windowsx.h>
  13. #include <commctrl.h>
  14. #include <dinput.h>
  15. #include "didevimg.h"
  16. #include "resource.h"
  17.  
  18. // {8C176D45-4AB1-4868-BCC1-C8ED85285BB1}
  19. static const GUID g_guidApp = 
  20. { 0x8c176d45, 0x4ab1, 0x4868, { 0xbc, 0xc1, 0xc8, 0xed, 0x85, 0x28, 0x5b, 0xb1 } };
  21.  
  22. // Full range of axis data values
  23. const int AXIS_RANGE = 200;
  24.  
  25.  
  26. //-----------------------------------------------------------------------------
  27. // Global variables
  28. //-----------------------------------------------------------------------------
  29. LPDIRECTINPUT8        g_pDI             = NULL;  // DirectInput access pointer
  30. LPDIRECTINPUTDEVICE8  g_pDevices[10]    = {0};   // List of devices
  31. DIACTIONFORMAT        g_diaf            = {0};   // DIACTIONFORMAT structure
  32. CDIDevImage           g_DIDevImages[10];         // Image drawing framework
  33. DWORD                 g_dwNumDevices    = 0;     // Total number of devices
  34. DWORD                 g_dwCurDevice     = 0;     // Index of current device
  35. HWND                  g_hDlg            = 0;     // Handle of the dialog
  36. HINSTANCE             g_hInst           = 0;     // Program instance
  37. DWORD                 g_dwCurTooltip    = 0;     // Current tooltip object
  38. DWORD                 g_dwCurHighlight  = 0;     // Currently highlighted object
  39. BOOL                  g_bHidden[10]     = {0};   // Stores the visual state of
  40.                                                  //   the checkbox
  41.  
  42.  
  43.  
  44. // Game action constants
  45. enum GAME_ACTIONS {
  46. WALK,               // Separate inputs are needed in this case for
  47. WALK_LEFT,          //   Walk/Left/Right because the joystick uses an
  48. WALK_RIGHT,         //   axis to report both left and right, but the
  49. BLOCK,              //   keyboard will use separate arrow keys.
  50. KICK, 
  51. PUNCH, 
  52. THE_DEAPPETIZER,    // "The De-Appetizer" represents a special move
  53. APOLOGIZE,          //   defined by this game.
  54. QUIT,
  55. NUM_OF_ACTIONS
  56. };
  57.  
  58. // Friendly names for action constants are used by DirectInput for the
  59. // built-in configuration UI
  60. const TCHAR *ACTION_NAMES[] =
  61. {
  62.     TEXT("Walk left/right"),
  63.     TEXT("Walk left"),
  64.     TEXT("Walk right"),
  65.     TEXT("Block"),
  66.     TEXT("Kick"),
  67.     TEXT("Punch"),
  68.     TEXT("\"The De-Appetizer\""),
  69.     TEXT("Apologize"),
  70.     TEXT("Quit")
  71. };
  72.  
  73.  
  74. // Map the game actions to the genre and keyboard constants.
  75. DIACTION g_adiaActionMap[] =
  76. {
  77.     // Device input (joystick, etc.) that is pre-defined by DInput according
  78.     // to genre type. The genre for this app is Action->Hand to Hand Fighting.
  79.     { WALK,             DIAXIS_FIGHTINGH_LATERAL,     0,  ACTION_NAMES[WALK], },
  80.     { BLOCK,            DIBUTTON_FIGHTINGH_BLOCK,     0,  ACTION_NAMES[BLOCK], },
  81.     { KICK,             DIBUTTON_FIGHTINGH_KICK,      0,  ACTION_NAMES[KICK], },
  82.     { PUNCH,            DIBUTTON_FIGHTINGH_PUNCH,     0,  ACTION_NAMES[PUNCH], },
  83.     { THE_DEAPPETIZER,  DIBUTTON_FIGHTINGH_SPECIAL1,  0,  ACTION_NAMES[THE_DEAPPETIZER], },
  84.  
  85.     // Map the apologize button to any button on the device. DirectInput
  86.     // defines several "Any-Control Constants" for mapping game actions to
  87.     // any device object of a particular type.
  88.     { APOLOGIZE,        DIBUTTON_ANY(1),              0,  ACTION_NAMES[APOLOGIZE], },
  89.  
  90.     // Keyboard input mappings
  91.     { WALK_LEFT,        DIKEYBOARD_LEFT,              0,  ACTION_NAMES[WALK_LEFT], },
  92.     { WALK_RIGHT,       DIKEYBOARD_RIGHT,             0,  ACTION_NAMES[WALK_RIGHT], },
  93.     { BLOCK,            DIKEYBOARD_B,                 0,  ACTION_NAMES[BLOCK], },
  94.     { KICK,             DIKEYBOARD_K,                 0,  ACTION_NAMES[KICK], },
  95.     { PUNCH,            DIKEYBOARD_P,                 0,  ACTION_NAMES[PUNCH], },
  96.     { THE_DEAPPETIZER,  DIKEYBOARD_H,                 0,  ACTION_NAMES[THE_DEAPPETIZER], },
  97.     { APOLOGIZE,        DIKEYBOARD_A,                 0,  ACTION_NAMES[APOLOGIZE], },
  98.  
  99.     // The DIA_APPFIXED constant can be used to instruct DirectInput that the
  100.     // current mapping can not be changed by the user.
  101.     { QUIT,             DIKEYBOARD_Q,      DIA_APPFIXED,  ACTION_NAMES[QUIT], },
  102.  
  103.     // Mouse input mappings
  104.     { WALK,             DIMOUSE_XAXIS,                0,  ACTION_NAMES[WALK], },
  105.     { PUNCH,            DIMOUSE_BUTTON0,              0,  ACTION_NAMES[PUNCH], },
  106.     { KICK,             DIMOUSE_BUTTON1,              0,  ACTION_NAMES[KICK], },
  107. };
  108. inline DWORD GetNumOfMappings() { return sizeof(g_adiaActionMap)/sizeof(DIACTION); }
  109.  
  110. // Function prototypes
  111. BOOL    CALLBACK EnumDevicesCallback( LPCDIDEVICEINSTANCE, LPDIRECTINPUTDEVICE8, DWORD, DWORD, LPVOID );
  112. BOOL    CALLBACK InitAxes( LPCDIDEVICEOBJECTINSTANCE pddoi, LPVOID pvRef ); 
  113. HRESULT InitDirectInput();
  114. VOID    CheckInput();
  115. VOID    OnTabChange();
  116. VOID    OnViewChange();
  117. VOID    RefreshViews();
  118. VOID    ResizeDeviceImage();
  119. BOOL    CALLBACK ToggleUnmapped( LPCDIDEVICEOBJECTINSTANCE pddoi, LPVOID pvRef );
  120. VOID    Cleanup();
  121.  
  122.  
  123.  
  124.  
  125.  
  126. //-----------------------------------------------------------------------------
  127. // Name: MainDialogProc
  128. // Desc: Handles dialog messages
  129. //-----------------------------------------------------------------------------
  130. INT_PTR CALLBACK MainDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  131. {
  132.     POINT pt;
  133.     DWORD dwObj;
  134.     DWORD dwState;
  135.     RECT  rcImage = {0};
  136.     HWND  hwndImage = NULL;
  137.  
  138.     switch( msg ) 
  139.     {
  140.         case WM_INITDIALOG:
  141.  
  142.             g_hDlg = hDlg;
  143.  
  144.             // Load and set the icon
  145.             HICON hIcon;
  146.             hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  147.             SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  148.             SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  149.  
  150.             // Initialize DirectInput
  151.             if( FAILED( InitDirectInput() ) )
  152.             {
  153.                 MessageBox( NULL, TEXT("Error Initializing DirectInput"), 
  154.                             TEXT("DirectInput Sample"), MB_ICONERROR | MB_OK );
  155.                 EndDialog( hDlg, 0 );
  156.             }
  157.  
  158.             
  159.             // Set a timer to go off 30 times a second. At every timer message
  160.             // the input device will be read
  161.             SetTimer( hDlg, 0, 1000 / 30, NULL );
  162.  
  163.             return TRUE;
  164.  
  165.         case WM_TIMER:
  166.             // Update the input device every timer message
  167.             CheckInput();
  168.             return TRUE;
  169.  
  170.         case WM_DRAWITEM:
  171.             HDC hdc;
  172.  
  173.             hdc = ((LPDRAWITEMSTRUCT) lParam)->hDC;   
  174.             g_DIDevImages[ g_dwCurDevice ].RenderToDC( hdc );
  175.             return TRUE;
  176.  
  177.         case WM_MOUSEMOVE:      
  178.             dwObj = 0;
  179.             dwState = 0;
  180.  
  181.             hwndImage = GetDlgItem( hDlg, IDC_IMAGE );
  182.             GetWindowRect( hwndImage, &rcImage );
  183.                
  184.             pt.x = GET_X_LPARAM(lParam);
  185.             pt.y = GET_Y_LPARAM(lParam);
  186.  
  187.             ClientToScreen( hDlg, &pt );
  188.  
  189.             pt.x -= rcImage.left;
  190.             pt.y -= rcImage.top;
  191.  
  192.             // Tooltip logic
  193.             
  194.             // If the current mouse pointer is within an object callout
  195.             if( SUCCEEDED( g_DIDevImages[ g_dwCurDevice ].GetObjFromPoint( pt, &dwObj ) ) )
  196.             {
  197.                 // And if the mouse is over a different object than the current tooltip
  198.                 // object
  199.                 if( g_dwCurTooltip != dwObj )
  200.                 {         
  201.                     // Remove the tooltip from the last object
  202.                     g_DIDevImages[ g_dwCurDevice ].GetCalloutState( g_dwCurTooltip, &dwState );
  203.                     g_DIDevImages[ g_dwCurDevice ].SetCalloutState( g_dwCurTooltip, dwState & ~DIDICOS_TOOLTIP );
  204.                     g_dwCurTooltip = 0;
  205.  
  206.                     // Set the tooltip for the current object
  207.                     g_DIDevImages[ g_dwCurDevice ].GetCalloutState( dwObj, &dwState );
  208.                     g_DIDevImages[ g_dwCurDevice ].SetCalloutState( dwObj, dwState | DIDICOS_TOOLTIP );
  209.                     g_dwCurTooltip = dwObj;
  210.  
  211.                     // Repaint
  212.                     InvalidateRect( hwndImage, NULL, FALSE );
  213.                 }
  214.             }
  215.             else
  216.             {
  217.                 // The mouse isn't over any callouts. If there is a current tooltip object,
  218.                 // clear it.
  219.                 if( g_dwCurTooltip != 0 )
  220.                 {   
  221.                     g_DIDevImages[ g_dwCurDevice ].GetCalloutState( g_dwCurTooltip, &dwState );
  222.                     g_DIDevImages[ g_dwCurDevice ].SetCalloutState( g_dwCurTooltip, dwState & ~DIDICOS_TOOLTIP );
  223.                     g_dwCurTooltip = 0;
  224.  
  225.                     // Repaint
  226.                     InvalidateRect( hwndImage, NULL, FALSE );
  227.                 }
  228.             }
  229.  
  230.             return TRUE; // Message handled
  231.  
  232.         case WM_LBUTTONDOWN:
  233.             
  234.             dwObj = 0;
  235.             dwState = 0;
  236.  
  237.             hwndImage = GetDlgItem( hDlg, IDC_IMAGE );
  238.             GetWindowRect( hwndImage, &rcImage );
  239.                
  240.             pt.x = GET_X_LPARAM(lParam); 
  241.             pt.y = GET_Y_LPARAM(lParam); 
  242.  
  243.             ClientToScreen( hDlg, &pt );
  244.  
  245.             pt.x -= rcImage.left;
  246.             pt.y -= rcImage.top;
  247.  
  248.             if( SUCCEEDED( g_DIDevImages[ g_dwCurDevice ].GetObjFromPoint( pt, &dwObj ) ) )
  249.             {
  250.                 // We have selected a new object to be highlighted. Un-highlight the old one
  251.                 if( g_dwCurHighlight )
  252.                 {
  253.                     g_DIDevImages[ g_dwCurDevice ].GetCalloutState( g_dwCurHighlight, &dwState );
  254.                     g_DIDevImages[ g_dwCurDevice ].SetCalloutState( g_dwCurHighlight, dwState & ~DIDICOS_HIGHLIGHTED );
  255.                 }
  256.  
  257.                 // Set the highlight flag
  258.                 g_dwCurHighlight = dwObj;
  259.                 g_DIDevImages[ g_dwCurDevice ].GetCalloutState( dwObj, &dwState );
  260.                 g_DIDevImages[ g_dwCurDevice ].SetCalloutState( dwObj, dwState | DIDICOS_HIGHLIGHTED );
  261.             
  262.                 // Repaint
  263.                 InvalidateRect( hwndImage, NULL, FALSE );
  264.             }
  265.  
  266.             return TRUE;
  267.  
  268.         case WM_COMMAND:
  269.             switch( LOWORD(wParam) )
  270.             {
  271.                 case IDCANCEL:
  272.                     EndDialog( hDlg, 0 );
  273.                     return TRUE;
  274.  
  275.                 case IDC_HIDE:
  276.                     // User has selected to toggle the visibility of device 
  277.                     // objects which were not mapped to actions
  278.                     
  279.                     // Build the action map against the device, and enumerate
  280.                     // through the objects with the ToggleUnmapped callback
  281.                     if( SUCCEEDED(g_pDevices[ g_dwCurDevice ]->BuildActionMap( &g_diaf, NULL, DIDBAM_DEFAULT )) )    
  282.                         g_pDevices[ g_dwCurDevice ]->EnumObjects( ToggleUnmapped, 0, DIDFT_ALL );
  283.                 
  284.                     // Store checkbox state
  285.                     g_bHidden[ g_dwCurDevice ] = !g_bHidden[ g_dwCurDevice ];
  286.  
  287.                     // This could have changed the number of views
  288.                     RefreshViews();
  289.  
  290.                     // Repaint
  291.                     hwndImage = GetDlgItem( g_hDlg, IDC_IMAGE );
  292.                     InvalidateRect( hwndImage, NULL, FALSE );   
  293.                     return TRUE;
  294.       
  295.                 case IDC_CURVIEW:
  296.                     switch( HIWORD(wParam) )
  297.                     {
  298.                         case EN_CHANGE:
  299.                             // This could be reached before INITDIALOG
  300.                             if( g_hDlg )
  301.                                 OnViewChange();
  302.                             return TRUE;
  303.                     }
  304.                     break;
  305.             }
  306.             break;
  307.  
  308.         case WM_NOTIFY:
  309.             switch( wParam )
  310.             {
  311.                 case IDC_DEVICES:
  312.                     switch( ((LPNMHDR)lParam)->code )
  313.                     {
  314.                         case TCN_SELCHANGE:
  315.                             // User has selected one of the device tabs
  316.                             OnTabChange();
  317.                             return TRUE;
  318.                 
  319.                     }
  320.                     break;
  321.             }
  322.             break;
  323.  
  324.         case WM_DESTROY:
  325.             // Cleanup everything
  326.             KillTimer( hDlg, 0 );    
  327.             Cleanup();    
  328.             return TRUE;    
  329.     }
  330.  
  331.     return FALSE; // Message not handled 
  332. }
  333.  
  334.  
  335.  
  336.  
  337.  
  338. //-----------------------------------------------------------------------------
  339. // Name: WinMain()
  340. // Desc: Entry point for the application.  Since we use a simple dialog for 
  341. //       user interaction we don't need to pump messages.
  342. //-----------------------------------------------------------------------------
  343. int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, int )
  344. {
  345.     g_hInst = hInst;
  346.  
  347.     InitCommonControls();
  348.  
  349.     // Display the main dialog box.
  350.     DialogBox( hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, MainDlgProc );
  351.     
  352.     return TRUE;
  353. }
  354.  
  355.  
  356.  
  357.  
  358.  
  359. //-----------------------------------------------------------------------------
  360. // Name: InitDirectInput
  361. // Desc: Initialize the DirectInput variables.
  362. //-----------------------------------------------------------------------------
  363. HRESULT InitDirectInput()
  364. {
  365.     HRESULT hr;
  366.  
  367.     // Register with the DirectInput subsystem and get a pointer to an 
  368.     // IDirectInput interface we can use.
  369.     if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
  370.                                          IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
  371.         return hr;
  372.  
  373.  
  374.     // ************************************************************************
  375.     // Step 3: Enumerate Devices.
  376.     // 
  377.     // Enumerate through devices according to the desired action map.
  378.     // Devices are enumerated in a prioritized order, such that devices which
  379.     // can best be mapped to the provided action map are returned first.
  380.     // ************************************************************************
  381.  
  382.     // Setup action format for the actual gameplay
  383.     ZeroMemory( &g_diaf, sizeof(DIACTIONFORMAT) );
  384.     g_diaf.dwSize          = sizeof(DIACTIONFORMAT);
  385.     g_diaf.dwActionSize    = sizeof(DIACTION);
  386.     g_diaf.dwDataSize      = GetNumOfMappings() * sizeof(DWORD);
  387.     g_diaf.guidActionMap   = g_guidApp;
  388.     g_diaf.dwGenre         = DIVIRTUAL_FIGHTING_HAND2HAND; 
  389.     g_diaf.dwNumActions    = GetNumOfMappings();
  390.     g_diaf.rgoAction       = g_adiaActionMap;
  391.     g_diaf.lAxisMin        = -99;
  392.     g_diaf.lAxisMax        = 99;
  393.     g_diaf.dwBufferSize    = 16;
  394.     lstrcpy( g_diaf.tszActionMap, TEXT("ActionMap Sample") );
  395.  
  396.        
  397.     if( FAILED( hr = g_pDI->EnumDevicesBySemantics( NULL, &g_diaf,  
  398.                                                     EnumDevicesCallback,
  399.                                                     NULL, DIEDBSFL_ATTACHEDONLY ) ) )
  400.         return hr;
  401.     
  402.     // For each detected device, set the display settings
  403.     RECT rc = {0};
  404.     ResizeDeviceImage();
  405.  
  406.     GetWindowRect( GetDlgItem( g_hDlg, IDC_IMAGE ), &rc );
  407.  
  408.     for( UINT i=0; i < g_dwNumDevices; i++ )
  409.     { 
  410.         // Set the output image size to match the client area of the tab control
  411.         g_DIDevImages[i].SetOutputImageSize( rc.right - rc.left, 
  412.                                              rc.bottom - rc.top, 
  413.                                              DIDISOIS_RESIZE );
  414.  
  415.         // Set the background color to match the dialog background. Since the 
  416.         // device image framework allows for transparent backgrounds, the background
  417.         // color is passed as a D3DCOLOR value. The framework provides an function
  418.         // to convert between D3DCOLOR and COLORREF types.
  419.         g_DIDevImages[i].SetColors( ColorFromCR( GetSysColor( COLOR_BTNFACE ) ), 
  420.                                     RGB(100, 100, 100), RGB(200, 0, 0) );
  421.     }
  422.     OnTabChange();
  423.  
  424.     return S_OK;
  425. }
  426.  
  427.  
  428.  
  429.  
  430. //-----------------------------------------------------------------------------
  431. // Name: EnumDevicesCallback
  432. // Desc: Callback function for EnumDevices. This particular function stores
  433. //       a list of all currently attached devices for use on the input chart.
  434. //-----------------------------------------------------------------------------
  435. BOOL CALLBACK EnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi, 
  436.                                   LPDIRECTINPUTDEVICE8 lpdid, DWORD dwFlags, 
  437.                                   DWORD dwRemaining, LPVOID pvRef )
  438. {
  439.     HRESULT hr;
  440.     TCITEM tcItem         = {0};
  441.  
  442.     // Build the action map against the device
  443.     if( FAILED(hr = lpdid->BuildActionMap( &g_diaf, NULL, DIDBAM_DEFAULT )) )
  444.         // There was an error while building the action map. Ignore this
  445.         // device, and contine with the enumeration
  446.         return DIENUM_CONTINUE;
  447.  
  448.     // Set an appropriate data format for the device. Although we're using 
  449.     // action mapping in order to label the device objects with game actions,
  450.     // we need to use standard input methods in order to retrieve input from
  451.     // unmapped device objects.
  452.     switch( 0xff & lpddi->dwDevType )
  453.     {
  454.     case DI8DEVTYPE_KEYBOARD:
  455.         lpdid->SetDataFormat( &c_dfDIKeyboard );
  456.         break;
  457.         
  458.     case DI8DEVTYPE_MOUSE:
  459.         lpdid->SetDataFormat( &c_dfDIMouse2 );
  460.         break;
  461.  
  462.     default:
  463.         lpdid->SetDataFormat( &c_dfDIJoystick2 );
  464.         break;
  465.     }
  466.  
  467.     // Set device properties
  468.     DIPROPDWORD dipdw = {0};
  469.     dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  470.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  471.     dipdw.diph.dwHow = DIPH_DEVICE;
  472.     dipdw.dwData = 16;
  473.     
  474.     // Allocate a buffer for input events
  475.     hr = lpdid->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
  476.     if( FAILED(hr) )
  477.         return DIENUM_CONTINUE;
  478.  
  479.     dipdw.dwData = DIPROPAXISMODE_REL;
  480.     hr = lpdid->SetProperty( DIPROP_AXISMODE, &dipdw.diph );
  481.     if( FAILED(hr) )
  482.         return DIENUM_CONTINUE;
  483.     
  484.     // Set the range and axis mode for each axis object
  485.     lpdid->EnumObjects( InitAxes, lpdid, DIDFT_AXIS );
  486.  
  487.     // Initialize the device image frame for the current device
  488.     hr = g_DIDevImages[g_dwNumDevices].Init( lpdid );
  489.     if( FAILED(hr) )
  490.         return DIENUM_CONTINUE;
  491.  
  492.     // Enumerate through the actions to set the callout strings
  493.     for( UINT i=0; i < g_diaf.dwNumActions; i++) 
  494.     {
  495.         DIACTION *dia = &g_diaf.rgoAction[i];
  496.         g_DIDevImages[g_dwNumDevices].SetCalloutText( dia->dwObjID, dia->lptszActionName );
  497.     } 
  498.     
  499.     // The current device has been successfully mapped. By storing the
  500.     // pointer and informing COM that we've added a reference to the 
  501.     // device, we can use this pointer later when gathering input.
  502.     g_pDevices[g_dwNumDevices] = lpdid;
  503.     lpdid->AddRef();
  504.  
  505.     tcItem.mask    = TCIF_TEXT;
  506.     tcItem.pszText = (TCHAR*) lpddi->tszInstanceName;
  507.  
  508.     // Create a tab for this device
  509.     SendMessage( GetDlgItem( g_hDlg, IDC_DEVICES ), TCM_INSERTITEM, (WPARAM) g_dwNumDevices, (LPARAM) &tcItem );
  510.  
  511.     // Increment the global device counter
  512.     g_dwNumDevices++;
  513.  
  514.     // Ask for the next device
  515.     return DIENUM_CONTINUE;
  516. }
  517.  
  518.  
  519.  
  520.  
  521. //-----------------------------------------------------------------------------
  522. // Name: InitAxes
  523. // Desc: Callback for EnumObjects sets the axis range and relative input mode.
  524. //-----------------------------------------------------------------------------
  525. BOOL CALLBACK InitAxes( LPCDIDEVICEOBJECTINSTANCE pddoi, LPVOID pvRef )
  526. {
  527.     DIPROPRANGE dipr = {0};
  528.     LPDIRECTINPUTDEVICE8 pdid = (LPDIRECTINPUTDEVICE8) pvRef;
  529.  
  530.     dipr.diph.dwSize = sizeof(DIPROPRANGE);
  531.     dipr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  532.     dipr.diph.dwObj = pddoi->dwType;
  533.     dipr.diph.dwHow = DIPH_BYID;
  534.     dipr.lMin = - (AXIS_RANGE / 2);
  535.     dipr.lMax = (AXIS_RANGE / 2);
  536.  
  537.     pdid->SetProperty( DIPROP_RANGE, &dipr.diph );
  538.     
  539.     return DIENUM_CONTINUE;
  540. }
  541.  
  542.  
  543.  
  544.  
  545. //-----------------------------------------------------------------------------
  546. // Name: CheckInput
  547. // Desc: Poll the attached devices and update the display
  548. //-----------------------------------------------------------------------------
  549. void CheckInput()
  550. {
  551.     HRESULT hr;
  552.     BOOL bRender = FALSE;
  553.  
  554.     LPDIRECTINPUTDEVICE8 pdidDevice = g_pDevices[g_dwCurDevice];
  555.     if( !pdidDevice )
  556.         return;
  557.  
  558.     DIDEVICEOBJECTDATA rgdod[16];
  559.     DWORD   dwItems = 16;
  560.  
  561.     hr = pdidDevice->Acquire();  
  562.     hr = pdidDevice->Poll();
  563.     hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
  564.                                     rgdod, &dwItems, 0 );
  565.  
  566.     
  567.     // GetDeviceData can fail for several reasons, some of which are
  568.     // expected during a program's execution. A device's acquisition is not
  569.     // permanent, and your program might need to reacquire a device several
  570.     // times. Since this sample is polling frequently, an attempt to
  571.     // acquire a lost device will occur during the next call to CheckInput.
  572.     if( FAILED(hr) )
  573.         return;
  574.  
  575.     bRender = FALSE;
  576.     for( DWORD j=0; j<dwItems; j++ )
  577.     {
  578.         bRender = TRUE;
  579.         DIDEVICEOBJECTINSTANCE didoi = {0};
  580.         didoi.dwSize = sizeof(DIDEVICEOBJECTINSTANCE);
  581.  
  582.         hr = pdidDevice->GetObjectInfo( &didoi, rgdod[j].dwOfs, DIPH_BYOFFSET );
  583.         if( SUCCEEDED(hr) )
  584.         {
  585.             DWORD dwState = 0;
  586.             BOOL  bHighlight = FALSE;
  587.             
  588.             // Determine whether the device object is active based on the 
  589.             // object type
  590.             if( didoi.dwType & DIDFT_BUTTON )
  591.                 bHighlight = rgdod[j].dwData & 0x80;
  592.             else if( didoi.dwType & DIDFT_POV )
  593.                 bHighlight = rgdod[j].dwData != -1;
  594.             else if( didoi.dwType & DIDFT_AXIS )
  595.             {
  596.                 // Steering wheels and pedals are often harder to move
  597.                 // than joystick axes, and sometimes have half the 
  598.                 // resolution (so the full range of movement might be 
  599.                 // reported across half the data range). To compensate, 
  600.                 // we'll scale the threshold accordingly.
  601.                 if( didoi.dwType & DI8DEVTYPE_DRIVING )
  602.                     bHighlight = labs( rgdod[j].dwData ) > (AXIS_RANGE / 20);
  603.                 else
  604.                     bHighlight = labs( rgdod[j].dwData ) > (AXIS_RANGE / 5);
  605.             }
  606.           
  607.  
  608.             // Set or clear the highlight flag based on the object state
  609.             if( bHighlight )
  610.             {
  611.                 // Un-highlight the currently highlighted object
  612.                 if( g_dwCurHighlight )
  613.                 {
  614.                     g_DIDevImages[g_dwCurDevice].GetCalloutState( g_dwCurHighlight, &dwState );
  615.                     g_DIDevImages[g_dwCurDevice].SetCalloutState( g_dwCurHighlight, dwState & ~DIDICOS_HIGHLIGHTED );
  616.                 }
  617.  
  618.                 g_dwCurHighlight = didoi.dwType;
  619.                 g_DIDevImages[g_dwCurDevice].GetCalloutState( didoi.dwType, &dwState );
  620.                 g_DIDevImages[g_dwCurDevice].SetCalloutState( didoi.dwType, dwState | DIDICOS_HIGHLIGHTED );
  621.             }   
  622.         }
  623.     }
  624.  
  625.     if( bRender )
  626.     {
  627.         HWND hwndImage = GetDlgItem( g_hDlg, IDC_IMAGE );
  628.         InvalidateRect( hwndImage, NULL, FALSE );   
  629.     }
  630. }
  631.  
  632.  
  633.  
  634.  
  635. //-----------------------------------------------------------------------------
  636. // Name: OnTabChange
  637. // Desc: Handles tab change messages for switching the current device
  638. //-----------------------------------------------------------------------------
  639. VOID OnTabChange()
  640. {
  641.     HWND hwndTabs =  GetDlgItem( g_hDlg, IDC_DEVICES );
  642.     HWND hwndImage = GetDlgItem( g_hDlg, IDC_IMAGE );
  643.     HWND hwndHide  = GetDlgItem( g_hDlg, IDC_HIDE );
  644.    
  645.     // We're changing devices. Un-highlight the currently highlighted object
  646.     if( g_dwCurHighlight )
  647.     {
  648.         DWORD dwState;
  649.         g_DIDevImages[ g_dwCurDevice ].GetCalloutState( g_dwCurHighlight, &dwState );
  650.         g_DIDevImages[ g_dwCurDevice ].SetCalloutState( g_dwCurHighlight, dwState & ~DIDICOS_HIGHLIGHTED );
  651.         g_dwCurHighlight = 0;
  652.     }
  653.  
  654.     // Get the currently selected device index
  655.     g_dwCurDevice = SendMessage( hwndTabs, TCM_GETCURSEL, 0, 0 );
  656.  
  657.     // Set the checkbox state
  658.     SendMessage( hwndHide, BM_SETCHECK, 
  659.                  (WPARAM) ( g_bHidden[ g_dwCurDevice ] ? BST_CHECKED : BST_UNCHECKED ), 0 );
  660.  
  661.     RefreshViews();
  662.  
  663.     // Repaint and clear background
  664.     InvalidateRect( hwndImage, NULL, TRUE );
  665. }
  666.  
  667.  
  668.  
  669.  
  670. //-----------------------------------------------------------------------------
  671. // Name: OnViewChange
  672. // Desc: Handles view change requests from the user
  673. //-----------------------------------------------------------------------------
  674. VOID OnViewChange()
  675. {
  676.     TCHAR strNextView[10] = {0};
  677.     DWORD dwNextView = 0;
  678.  
  679.     DWORD dwCurView, dwNumOfViews;
  680.  
  681.     g_DIDevImages[ g_dwCurDevice ].GetActiveView( &dwCurView, &dwNumOfViews );
  682.     dwCurView++; // Stored internally as an index. We need to increment.
  683.  
  684.     // The requested view is the current value of the IDC_CURVIEW control
  685.     GetWindowText( GetDlgItem( g_hDlg, IDC_CURVIEW ), strNextView, 10 );
  686.     dwNextView = _ttoi( strNextView );
  687.  
  688.     if( dwNextView != dwCurView )
  689.     {
  690.         // A new view has been requested. The number may or may not be valid.
  691.         if( dwNextView > 0 && dwNextView <= dwNumOfViews )
  692.         {
  693.             // Valid request. Change the current view and repaint.
  694.             // The offset is due to the discrpancy between viewed values and
  695.             // internal indices.
  696.             g_DIDevImages[ g_dwCurDevice ].SetActiveView( dwNextView-1 );
  697.             InvalidateRect( GetDlgItem( g_hDlg, IDC_IMAGE ), NULL, FALSE );
  698.         }
  699.         else
  700.         {
  701.             // Invalid request. Revert the screen value to the current view.
  702.             _itot( dwCurView, strNextView, 10 );
  703.             SetWindowText( GetDlgItem( g_hDlg, IDC_CURVIEW ), strNextView );
  704.         }
  705.     }
  706. }
  707.  
  708.  
  709.  
  710.  
  711. //-----------------------------------------------------------------------------
  712. // Name: RefreshViews
  713. // Desc: Called after an action is taken that might change the number of views
  714. //       available for the current device
  715. //-----------------------------------------------------------------------------
  716. VOID RefreshViews()
  717. {
  718.     DWORD dwCurView, dwNumOfViews;
  719.     TCHAR strCurView[10], strNumOfViews[10];
  720.  
  721.     g_DIDevImages[ g_dwCurDevice ].GetActiveView( &dwCurView, &dwNumOfViews );
  722.     
  723.     // If the current view is invalid, reset
  724.     if( dwCurView >= dwNumOfViews )
  725.         g_DIDevImages[ g_dwCurDevice ].SetActiveView( dwCurView = 0 );
  726.         
  727.     dwCurView++; // Views are indexed from 0 up
  728.  
  729.     _itot( dwCurView, strCurView, 10 ); 
  730.     _itot( dwNumOfViews, strNumOfViews, 10 );
  731.   
  732.     // Set the values for the current view, and number of views
  733.     SetWindowText( GetDlgItem( g_hDlg, IDC_CURVIEW ), strCurView );
  734.     SetWindowText( GetDlgItem( g_hDlg, IDC_MAXVIEW ), strNumOfViews );
  735.  
  736.     // Set the spin control ranges
  737.     SendMessage( GetDlgItem( g_hDlg, IDC_SPIN ), UDM_SETRANGE, 0, 
  738.                  (LPARAM) MAKELONG( (SHORT) dwCurView, (SHORT) dwNumOfViews ) );
  739. }
  740.  
  741.  
  742.  
  743.  
  744. //-----------------------------------------------------------------------------
  745. // Name: ToggleUnmapped
  746. // Desc: Callback function cycles through device objects and negates the
  747. //       current visible flag for objects which are not mapped to an action.
  748. //-----------------------------------------------------------------------------
  749. BOOL CALLBACK ToggleUnmapped( LPCDIDEVICEOBJECTINSTANCE pddoi, LPVOID pvRef )
  750. {
  751.  
  752.     // If action array has an object ID which matches the enumerated ID, then
  753.     // the current device object has a corresponding action.
  754.     for( UINT i=0; i < g_diaf.dwNumActions; i++) 
  755.     {
  756.         DIACTION *pdia = &g_diaf.rgoAction[i];
  757.  
  758.         if( pdia->dwHow == DIAH_ERROR || pdia->dwHow == DIAH_UNMAPPED )
  759.             continue;
  760.         
  761.         if( pdia->dwObjID == pddoi->dwType )
  762.             return DIENUM_CONTINUE;
  763.  
  764.     }
  765.  
  766.     // We're still here, so this object is unmapped
  767.     DWORD dwState = 0;
  768.     g_DIDevImages[ g_dwCurDevice ].GetCalloutState( pddoi->dwType, &dwState );
  769.  
  770.     if( dwState & DIDICOS_INVISIBLE )
  771.         dwState &= ~DIDICOS_INVISIBLE;
  772.     else
  773.         dwState |= DIDICOS_INVISIBLE;
  774.  
  775.     g_DIDevImages[ g_dwCurDevice ].SetCalloutState( pddoi->dwType, dwState );
  776.  
  777.     return DIENUM_CONTINUE;
  778. }
  779.  
  780.  
  781.  
  782.  
  783. //-----------------------------------------------------------------------------
  784. // Name: ResizeDeviceImage()
  785. // Desc: Adjust the size and position of the device image based on the tabs
  786. //-----------------------------------------------------------------------------
  787. VOID    ResizeDeviceImage()
  788. {
  789.     // Based on the number of tabs, the tab control has a different client
  790.     // size. Determine the current size and reposition the IDC_IMAGE control, 
  791.     // which is being used as the background for the device image.
  792.  
  793.     RECT rc;
  794.     HWND hwndTabs = GetDlgItem( g_hDlg, IDC_DEVICES );
  795.     GetWindowRect( hwndTabs, &rc );
  796.     TabCtrl_AdjustRect( hwndTabs, FALSE, &rc );
  797.  
  798.     POINT pt = { rc.left, rc.top };
  799.     ScreenToClient( g_hDlg, &pt );
  800.  
  801.     OffsetRect( &rc, pt.x - rc.left, pt.y - rc.top);
  802.     InflateRect( &rc, -10, -10 );
  803.     MoveWindow( GetDlgItem( g_hDlg, IDC_IMAGE ), rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE );
  804.  
  805. }
  806.  
  807.  
  808.  
  809.  
  810. //-----------------------------------------------------------------------------
  811. // Name: Cleanup()
  812. // Desc: Releases all previously initialized objects
  813. //-----------------------------------------------------------------------------
  814. VOID Cleanup()
  815. {
  816.     for( UINT i=0; i < g_dwNumDevices; i++ )
  817.     {
  818.         if( g_pDevices[i] )
  819.         {
  820.             g_pDevices[i]->Unacquire();
  821.             g_pDevices[i]->Release();
  822.             g_pDevices[i] = NULL;
  823.         }
  824.     }
  825.  
  826.     SAFE_RELEASE( g_pDI );
  827.  
  828.     
  829. }
  830.  
  831.  
  832.  
  833.